package org.yzh.commons.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Map;

public class HttpUtils {

    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    private static final String Separator = System.lineSeparator();

    private static final int ConnectTimeout = 3000;
    private static final int ReadTimeout = 3000;

    public static String doGet(String url) {
        return executeInternal("GET", url, null);
    }

    public static String doGet(String url, String... params) {
        url = NetUtils.buildUrl(url, params);
        return executeInternal("GET", url, null);
    }

    public static String doGet(String url, Map<String, String> params) {
        url = NetUtils.buildUrl(url, params);
        return executeInternal("GET", url, null);
    }

    public static String doPost(String url, String requestBody) {
        return executeInternal("POST", url, requestBody);
    }

    public static String doPost(String url, String... params) {
        url = NetUtils.buildUrl(url, params);
        return executeInternal("POST", url, null);
    }

    public static String doPost(String url, Map<String, String> params) {
        url = NetUtils.buildUrl(url, params);
        return executeInternal("POST", url, null);
    }

    private static String executeInternal(String method, String url, String requestBody) {
        log.debug("method={}, url={}, requestBody={}", method, url, requestBody);

        HttpURLConnection connection = null;
        OutputStream os = null;
        BufferedReader reader = null;
        try {
            connection = createHttpConnection(url);
            connection.setRequestMethod(method);
            connection.setRequestProperty("Connection", "Close");
//            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("Accept-charset", StandardCharsets.UTF_8.name());

            if (requestBody != null) {
                byte[] bytes = requestBody.getBytes(StandardCharsets.UTF_8);
                connection.setRequestProperty("Content-length", String.valueOf(bytes.length));
                connection.setRequestProperty("Content-type", "application/json;charset=UTF-8");
                os = connection.getOutputStream();
                os.write(bytes);
                os.flush();
            } else {
                connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
            }

            reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(connection.getInputStream(), 4096), StandardCharsets.UTF_8));

            StringBuilder response = new StringBuilder(1024);
            String line;
            while ((line = reader.readLine()) != null)
                response.append(line).append(Separator);
            return response.toString();
        } catch (IOException e) {
            throw new RuntimeException("调用远程系统出错url=" + url, e);
        } finally {
            close(os);
            close(reader);
            if (connection != null)
                connection.disconnect();
        }
    }

    public static File download(String url, String filePath) {
        return download(url, new File(filePath));
    }

    public static File download(String url, File file) {
        HttpURLConnection connection = null;
        OutputStream os = null;
        InputStream is = null;
        try {
            connection = createHttpConnection(url);
            connection.setRequestMethod("GET");
            connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

            is = new BufferedInputStream(connection.getInputStream());
            os = new BufferedOutputStream(new FileOutputStream(file));

            byte[] buff = new byte[4096];
            int length;
            while ((length = is.read(buff)) != -1) {
                os.write(buff, 0, length);
                os.flush();
            }
            is.close();
            return file;
        } catch (IOException e) {
            if (file != null)
                file.delete();
            throw new RuntimeException("调用远程系统出错url=" + url, e);
        } finally {
            close(os);
            close(is);
            if (connection != null)
                connection.disconnect();
        }
    }

    private static HttpURLConnection createHttpConnection(String url) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setConnectTimeout(ConnectTimeout);
        connection.setReadTimeout(ReadTimeout);
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setInstanceFollowRedirects(false);

        if (connection instanceof HttpsURLConnection) {
            HttpsURLConnection httpsConn = (HttpsURLConnection) connection;
            httpsConn.setSSLSocketFactory(SSL.Instance.SSLFactory);
            httpsConn.setHostnameVerifier(SSL.Instance.SimpleVerifier);
        }

        return connection;
    }

    public static void close(Closeable a) {
        if (a != null)
            try {
                a.close();
            } catch (IOException e) {
                log.warn("Closeable Error", e);
            }
    }

    private enum SSL {
        Instance;
        protected SSLHandler SimpleVerifier = new SSLHandler();
        protected SSLSocketFactory SSLFactory;

        SSL() {
            try {
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{SimpleVerifier}, null);
                SSLFactory = sslContext.getSocketFactory();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        private class SSLHandler implements X509TrustManager, HostnameVerifier {
            private SSLHandler() {
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            /**
             * 可接受的 CA 发行者证书的非 null（可能为空）的数组
             */
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            /**
             * @param hostname 主机名
             * @param session  到主机的连接上使用的 SSLSession
             * @return 如果主机名是可接受的，则返回 true
             */
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        }
    }
}